package com.fubowen.reader.extension

import com.fubowen.reader.db.model.Tree
import com.fubowen.reader.db.model.toTreeItem
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileFilter
import java.util.concurrent.ConcurrentLinkedQueue

val textExtension = setOf("txt", "epub")
var textFileFilter = FileFilter { file -> file.isDirectory || textExtension.contains(file.extension) }

//region searchFilePFS

suspend fun searchFilePFS(file: File, maxCoroutineCount: Int = 8): List<Tree> {
    val tempEnqueue = ConcurrentLinkedQueue<File>().apply { offer(file) }
    val semaphore = Semaphore(maxCoroutineCount)
    val items = mutableListOf<Tree>()
    return withContext(Dispatchers.IO) {
        while (tempEnqueue.isNotEmpty()) {
            val enqueueIncludeList = tempEnqueue.toList()
            tempEnqueue.clear()
            val waitFinishJob = mutableListOf<Deferred<Boolean?>>()
            for (item in enqueueIncludeList) {
                if (item.isDirectory) {
                    val readJob = async {
                        try {
                            semaphore.acquire()
                            item.listFiles(textFileFilter)?.let { tempEnqueue.addAll(it) }
                        } finally {
                            semaphore.release()
                        }
                    }
                    waitFinishJob.add(readJob)
                } else {
                    items.add(item.toTreeItem())
                }
            }
            waitFinishJob.awaitAll()
        }
        items
    }

}

//endregion

//region searchFileBFS
fun searchFileBFS(file: File): List<Tree> {
    val enqueue = ConcurrentLinkedQueue<File>()
    val fileList = ConcurrentLinkedQueue<Tree>()
    enqueue.offer(file)
    while (enqueue.isNotEmpty()) {
        val file = enqueue.poll()
        if (file.isDirectory) {
            file.listFiles(textFileFilter)?.forEach { enqueue.offer(it) }
        } else {
            fileList.add(file.toTreeItem())
        }
    }
    return fileList.toMutableList()
}
//endregion

//region searchFileDFS
fun searchFileDFS(file: File): List<Tree> {
    val fileList = mutableListOf<Tree>()
    if (file.isDirectory) {
        file.listFiles(textFileFilter)?.forEach { fileList += searchFileDFS(it) }
    } else {
        file.toTreeItem().apply {
            fileList.add(this)
        }
    }
    return fileList
}
//endregion

fun getFileList(book: Tree) {
    if (book.isRead) {
        return
    }
    val file = File(book.absolutePath)
    if (file.isDirectory) {
        file.listFiles(textFileFilter)
            ?.map { it.toTreeItem() }
            ?.sortedWith(compareBy<Tree> { it.name.startsWith(".") }.thenBy { it.name.lowercase() })
            ?.let { book.children += it }
    }
    book.isRead = true
}

